package com.wang.java.thread.threadlocal;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试ThreadLocal内存泄露:
 * 1.普通线程场景下：普通线程用完就随之销毁，Thread被销毁，Thread.threadLocals占用的内存也被销毁，所以没有发生OOM
 * 2.线程池场景下： 线程池中线程用完不会被销毁，FullGC后OOM
 * 如何避免内存泄露：使用完threadLocal要remove()
 */
public class ThreadLocalMemoryLeak {

    static ThreadLocal<byte[]> threadLocalBytes = new ThreadLocal<>();

    public static void main(String[] args) {
//        testMemoryLeakWithCommonThread();
//        testMemoryLeakWithThreadPool();
        testNoMemoryLeakWithThreadPool();
    }

    /**
     * 测试内存泄露（普通线程）
     */
    private static void testMemoryLeakWithCommonThread() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                threadLocalBytes.set(new byte[1014 * 1024]);
            }).start();
        }
    }

    /**
     * 测试内存泄露（线程池）
     */
    private static void testMemoryLeakWithThreadPool() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                threadLocalBytes.set(new byte[1014 * 1024]);
            });
        }
        executorService.shutdown();
    }

    /**
     * 避免内存泄露，使用完要remove()
     */
    private static void testNoMemoryLeakWithThreadPool() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(() -> {
                try {
                    threadLocalBytes.set(new byte[1014 * 1024]);
                } finally {
                    threadLocalBytes.remove();
                }
            });
        }
        executorService.shutdown();
    }
}
